﻿/*
		rCmd.inc - v0.1.2
		Copyright © 2012 RyDeR`
*/

#if defined _Included_rCmd
	#endinput
#else
	#define _Included_rCmd
#endif

#include "DynamicParams.inc"

#define MAX_FUNCTION_NAME (32)

#if !defined MAX_FUNCTIONS
	#define MAX_FUNCTIONS (2048)
#endif

#if !defined MAX_COMMAND_PARAMS
	#define MAX_COMMAND_PARAMS (8)
#endif

#if !defined MAX_STRING_LENGTH
	#define MAX_STRING_LENGTH (128)
#endif

stock
	g_aszFormat_rCmd[MAX_FUNCTIONS][MAX_COMMAND_PARAMS char]
;

#define rCmd[%0]->%1(%2) \
	cmd@%1();public cmd@%1(){new i=funcidx(#cmd_%1);if(-1!=i&&i<MAX_FUNCTIONS)strpack(g_aszFormat_rCmd[i],#%0,sizeof(g_aszFormat_rCmd[]));}cmd_%1(%2);public cmd_%1(%2)

public OnPlayerCommandText(playerid, cmdtext[]) {
	static
		bool: s_bInit,
		bool: s_bHasOPCR,
		bool: s_bHasOPCP,
		
		s_szFunc[MAX_FUNCTION_NAME]
	;
	if(!s_bInit) {
		for(new i = 0; i < MAX_FUNCTIONS; ++i) {
			if(!GetPublicNameByIndex(i, s_szFunc, sizeof(s_szFunc))) {
				continue;
			}
			if(strfind(s_szFunc, "cmd@") != -1) {
				CallLocalFunction(s_szFunc, "");
			}
		}
		if(!s_bHasOPCR) {
			s_bHasOPCR = !!(funcidx("OnPlayerCommandReceived") != -1);
		}
		if(!s_bHasOPCP) {
			s_bHasOPCP = !!(funcidx("OnPlayerCommandPerformed") != -1);
		}
		s_bInit = !s_bInit;
	}
	new
		iPos,
		iFunc
	;
	s_szFunc = "cmd_";
	
	while(cmdtext[++iPos] > ' ') {
		s_szFunc[iPos + 3] = tolower(cmdtext[iPos]);
	}
	while(cmdtext[iPos] == ' ') {
		iPos++;
	}
	iFunc = funcidx(s_szFunc);
	
	if(-1 != iFunc && iFunc < MAX_FUNCTIONS) {
		while(Pop_Param() != 0) {
			continue;
		}
		Push_Param(Mixed, playerid);
		
		static
			s_szFormat[MAX_COMMAND_PARAMS],
			s_aszParam[MAX_COMMAND_PARAMS][MAX_STRING_LENGTH],
			s_iIdx
		;
		s_szFormat[0] = s_iIdx = EOS;
		strunpack(s_szFormat, g_aszFormat_rCmd[iFunc], sizeof(s_szFormat));
		
		new
			iParams = strlen(s_szFormat),
			iFormat,
			i
		;
		if(iParams) {
			Push_Param(Mixed, true);
			
			while(i < iParams && cmdtext[iPos] != EOS) {
				switch(s_szFormat[iFormat]) {
					case EOS:
						break;
					case 'i', 'd':
						Push_Param(Mixed, strval(cmdtext[iPos]));
					case 'h': {
						new
							iValue
						;
						if(cmdtext[iPos] == '0' && cmdtext[iPos + 1] == 'x') {
							iPos += 2;
							
							while(('a' <= cmdtext[iPos] <= 'f' || 'A' <= cmdtext[iPos] <= 'F' || '0' <= cmdtext[iPos] <= '9')) {
								switch(cmdtext[iPos++]) {
									case 'a' .. 'f': {
										iValue = (iValue << 4) + (10 + cmdtext[iPos - 1] - 'a');
									}
									case 'A' .. 'F': {
										iValue = (iValue << 4) + (10 + cmdtext[iPos - 1] - 'A');
									}
									case '0' .. '9': {
										iValue = (iValue << 4) + (cmdtext[iPos - 1] - '0');
									}
								}
							}
						}
						Push_Param(Mixed, iValue);
					}
					case 'b': {
						new
							iValue
						;
						if(cmdtext[iPos] == '0' && cmdtext[iPos + 1] == 'b') {
							iPos += 2;
							
							while(('0' <= cmdtext[iPos] <= '1')) {
								iValue = (iValue << 1) | (cmdtext[iPos++] - '0');
							}
						}
						Push_Param(Mixed, iValue);
					}
					case 'u': {
						new
							iEnd = !s_szFormat[iFormat + 1] ? (EOS) : (' '),
							iIdx
						;
						static
							s_szInput[MAX_PLAYER_NAME],
							s_szName[MAX_PLAYER_NAME]
						;
						while(cmdtext[iPos] != iEnd) {
							s_szInput[iIdx++] = cmdtext[iPos++];
						}
						s_szInput[iIdx] = EOS;
						
						iIdx = INVALID_PLAYER_ID;
						
						if(IsNumeric(s_szInput)) {
							iIdx = strval(s_szInput);
						} else {
							for(new iPlayer = 0; iPlayer < MAX_PLAYERS; ++iPlayer) {
								if(GetPlayerName(iPlayer, s_szName, sizeof(s_szName))) {
									if(strfind(s_szName, s_szInput, true) != -1) {
										iIdx = i;
										break;
									}
								}
							}
						}
						Push_Param(Mixed, iIdx);
					}
					case 'f':
						Push_Param(Mixed, floatstr(cmdtext[iPos]));
					case 'c':
						Push_Param(Mixed, cmdtext[iPos]);
					case 's': {
						if(s_iIdx < MAX_COMMAND_PARAMS) {
							new
								iEnd = !s_szFormat[iFormat + 1] ? (EOS) : (' '),
								iIdx
							;
							while(cmdtext[iPos] != iEnd) {
								s_aszParam[s_iIdx][iIdx++] = cmdtext[iPos++];
							}
							s_aszParam[s_iIdx][iIdx] = EOS;
								
							Push_Param(String, s_aszParam[s_iIdx++]);
						}
					}
					default:
						goto Skip;
				}
				while(cmdtext[iPos] != EOS && cmdtext[iPos] != ' ')
					iPos++;
				while(cmdtext[iPos] == ' ')
					iPos++;
					
				++i;
					
			Skip:
				iFormat++;
			}
			if(s_szFormat[iFormat]) {
				new
					iPushed = Push_Param(Mixed, 0) - 2
				;
				Pop_Param();

				while(iPushed++ < iParams) {
					Push_Param(Mixed, 0);
				}
				Set_Param(Mixed, 1, false);
			}
		}
		iFormat = CallFunction(s_szFunc);
				
		if(s_bHasOPCP) {
			return CallLocalFunction("OnPlayerCommandPerformed", "isi", playerid, cmdtext, true);
		}
		return iFormat;
	} else {
		if(s_bHasOPCP) {
			return CallLocalFunction("OnPlayerCommandPerformed", "isi", playerid, cmdtext, false);
		}
	}
	return 0;
}

static stock GetPublicNameByIndex(const iIdx, szName[], const iSize = sizeof(szName)) {
	static
		s_iBase,
		s_iPublics,
		s_iNatives,
		s_iCount
	;
	if(!s_iBase) {
		#emit LCTRL 1
		#emit NEG
		#emit STOR.PRI s_iBase
	}
	if(!s_iPublics) {
		new
			iAddr
		;
		#emit LOAD.PRI s_iBase
		#emit ADD.C 32
		#emit STOR.S.PRI iAddr
		#emit LREF.S.PRI iAddr	
		#emit LOAD.S.ALT iAddr
		#emit ADD
		#emit CONST.ALT 32
		#emit SUB	
		#emit STOR.PRI s_iPublics
	}
	if(!s_iNatives) {
		new
			iAddr
		;
		#emit LOAD.PRI s_iBase
		#emit ADD.C 36
		#emit STOR.S.PRI iAddr
		#emit LREF.S.PRI iAddr	
		#emit LOAD.S.ALT iAddr
		#emit ADD
		#emit CONST.ALT 36
		#emit SUB
		#emit STOR.PRI s_iNatives
	}
	if(!s_iCount) {
		s_iCount = ((s_iNatives - s_iPublics) >>> 3);
	}
	if(iIdx < s_iCount) {
		new
			iPos = (iIdx << 3) + s_iPublics + 4,
			iCh,
			i
		;
		#emit LREF.S.PRI iPos
		#emit LOAD.ALT s_iBase
		#emit ADD
		#emit STOR.S.PRI iPos

		do {
			#emit LREF.S.PRI iPos
			#emit STOR.S.PRI iCh

			szName[i] = iCh & 0xFF;
				
			iPos++;
		} while(szName[i++] != EOS && i <= iSize);
		szName[i] = EOS;
		
		return 1;
	}
	return 0;
}

static stock IsNumeric(const szStr[]) {
	for(new i = 0, j = strlen(szStr); i < j; i++) {
		if(!('0' <= szStr[i] <= '9')) {
			return 0;
		}
	}
	return 1;
}

#if defined _ALS_OnPlayerCommandText
	#undef OnPlayerCommandText
#else
	#define _ALS_OnPlayerCommandText
#endif

#define OnPlayerCommandText rCmd_OnPlayerCommandText

forward rCmd_OnPlayerCommandText(playerid, cmdtext[]);

forward OnPlayerCommandReceived(playerid, cmdtext[]);
forward OnPlayerCommandPerformed(playerid, cmdtext[], success);